﻿/*-------------------------------------------------------------------------
* 命名空间: SiliconSelect.ViewModels/MainViewModel
* 类       名: MainViewModel
* 功      能:  TD
* 时      间:  2023/6/21 10:16:27
* 版      本:  V1.0
* 作      者:  Fioman
* 格      言:  Talk is cheap,show me the code ^_^
*-------------------------------------------------------------------------*/
using SiliconSelect.Base;
using SiliconSelect.BLL;
using SiliconSelect.Common;
using SiliconSelect.DAL;
using SiliconSelect.DAL.DataEntity;
using SiliconSelect.Models;
using SiliconSelect.PlcComunication;
using SiliconSelect.PlcComunication.Model;
using SiliconSelect.Views.UserOperatorDialog;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Media;

namespace SiliconSelect.ViewModels
{
    public class MainViewModel : NotifyBase
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public MainViewModel()
        {
            UserInfo = new UserModel();
            // 1. 切换窗口的命令
            NavChangedCommand = new Command(DoNavChanged);
            // 2. 切换用户的命令
            ChangeUserCommand = new Command(DoChangeUserCommand);
            // 3. 清空料盒指定料盒
            ClearOneBoxCommand = new Command(DoClearOneBoxTask);
            //4. 清空报警
            ClearAlarmCommand = new Command(DoClearAlarmTask);
            // 数据初始化
            InitializeData();
            // 直接显示首页
            DoNavChanged("MainPage");
            // 初始化一开始的状态
            Calibration3DStatus = "NG";
            CrystalCurrentIsChecked = true;
            RecipeComboxCanUse = false; // 默认是不可用的,必须点击才可以使用
            BoxInto = 1; // 默认是1号料盒
        }




        /// <summary>
        /// 中间显示的Box的高度
        /// </summary>
        private int _boxHeight;

        public int BoxHeight
        {
            get { return _boxHeight; }
            set { SetProperty(ref _boxHeight, value); }
        }

        /// <summary>
        /// 数量高度,主要数量高度是BoxHeight的两倍
        /// </summary>
        private int _countHeight;

        public int CountHeight
        {
            get { return _countHeight; }
            set { SetProperty(ref _countHeight, value); }
        }




        /// <summary>
        /// 中间显示的料盒的宽度. 要根据世界的屏幕尺寸去显示.
        /// </summary>
        private int _boxWidth;

        public int BoxWidth
        {
            get { return _boxWidth; }
            set { SetProperty(ref _boxWidth, value); }
        }

        /// <summary>
        /// 标题大字体
        /// </summary>
        private int _fontBoxBig;

        public int FontBoxBig
        {
            get { return _fontBoxBig; }
            set { SetProperty(ref _fontBoxBig, value); }
        }

        private int _fontBoxSmall;

        public int FontBoxSmall
        {
            get { return _fontBoxSmall; }
            set { SetProperty(ref _fontBoxSmall, value); }
        }

        /// <summary>
        /// 线体宽度
        /// </summary>
        private int _lineWidth;

        public int LineWidth
        {
            get { return _lineWidth; }
            set { SetProperty(ref _lineWidth, value); }
        }

        private int _lineHeight;

        public int LineHeight
        {
            get { return _lineHeight; }
            set { SetProperty(ref _lineHeight, value); }
        }


        private int _boardWidth;

        public int BoardWidth
        {
            get { return _boardWidth; }
            set { SetProperty(ref _boardWidth, value); }
        }

        private int _boardHeight;

        public int BoardHeight
        {
            get { return _boardHeight; }
            set { SetProperty(ref _boardHeight, value); }
        }

        private int _boardRadiusVal;

        public int BoardRadiusVal
        {
            get { return _boardRadiusVal; }
            set { SetProperty(ref _boardRadiusVal, value); }
        }

        /// <summary>
        /// 运行和停止按钮的尺寸
        /// </summary>
        private int _radioButtonFont;

        public int RadioButtonFont
        {
            get { return _radioButtonFont; }
            set { _radioButtonFont = value; }
        }

        /// <summary>
        /// 运行和停止文字的尺寸
        /// </summary>
        private int _radioButtonTextFont;

        public int RadioButtonTextFont
        {
            get { return _radioButtonTextFont; }
            set { _radioButtonTextFont = value; }
        }




        /// <summary>
        /// 显示当前的登录人
        /// </summary>
        private string? _currentLoginUser;

        public string? CurrentLoginUser
        {
            get { return _currentLoginUser; }
            set { SetProperty(ref _currentLoginUser, value); }
        }

        /// <summary>
        /// 显示系统时间
        /// </summary>
        private string? _systemTime;

        public string? SystemTime
        {
            get { return _systemTime; }
            set { SetProperty(ref _systemTime, value); }
        }


        /// <summary>
        /// PLC状态
        /// </summary>
        private PlcStateModel _plcState = PlcCommunication.Instance.PlcState;
        public PlcStateModel PlcState
        {
            get { return _plcState; }
            set { SetProperty(ref _plcState, value); }
        }


        /// <summary>
        /// 页面切换
        /// </summary>
        private FrameworkElement? _mainContent;

        public FrameworkElement? MainContent
        {
            get { return _mainContent; }
            set
            {
                SetProperty(ref _mainContent, value);
                if (_mainContent != null)
                {
                    PageTitle = _mainContent!.Tag.ToString()!;
                }
            }
        }

        /// <summary>
        /// 
        /// 
        /// 
        /// 所有的页面对象进行管理,如果这个界面对象已经存在了,就不要再创建了.
        /// </summary>
        public Dictionary<string, FrameworkElement> PageMapping = new Dictionary<string, FrameworkElement>();

        // 切换绑定命令
        public Command NavChangedCommand { get; set; }

        /// <summary>
        /// 页面的标题显示,用于在顶部显示要显示的页面,通过Tag属性来获取对应的值
        /// </summary>
        private string? _pageTitle;

        public string? PageTitle
        {
            get { return _pageTitle; }
            set { SetProperty(ref _pageTitle, value); }
        }



        /// <summary>
        /// 页面切换的时候要注意两个问题:
        /// 1) 点击同一个按钮,页面重复创建的问题.(去判断一下当前点击的按钮是否和要显示的页面是同一个页面)
        /// 2) 创建的对象不能重复使用.
        /// </summary>
        /// <param name="obj"></param>
        private void DoNavChanged(object obj)
        {
            if (obj != null && MainContent?.GetType().Name == (obj.ToString() + "View"))
                return;
            //如果页面已经存在就直接赋值就行.
            if (PageMapping.ContainsKey(obj!.ToString()!))
            {
                MainContent = PageMapping[obj!.ToString()!];
            }
            else
            {
                // 通过传递过来的参数来获取对应的View类,因为参数都么有写View,所以这里加了View
                Type? type = Type.GetType("SiliconSelect.Views." + obj!.ToString() + "View");
                ConstructorInfo? cti = type?.GetConstructor(Type.EmptyTypes);
                MainContent = (FrameworkElement)cti?.Invoke(null)!;
                PageMapping[obj!.ToString()!] = MainContent;
            }
        }
        /// <summary>
        /// 1. 登录的用户信息
        /// </summary>
        public UserModel? UserInfo { get; set; }

        // 2. WorkingInfo 当前作业信息
        public WorkingInfoModel WorkingInfo { get; set; } = new WorkingInfoModel();

        // 3. 屏蔽检测模块(已经屏蔽掉的检测模块)
        private ObservableCollection<string> _moduleShieldedList = new ObservableCollection<string>();

        public ObservableCollection<string> ModuleShieldedList
        {
            get { return _moduleShieldedList; }
            set { SetProperty(ref _moduleShieldedList, value); }
        }

        // 4. 所有检测模块(从数据库里面获取)
        private ObservableCollection<ModuleShieldedStateModel> _totalModuleShieldedStates =
          new ObservableCollection<ModuleShieldedStateModel>();

        public ObservableCollection<ModuleShieldedStateModel> TotalModuleShieldedStates
        {
            get { return _totalModuleShieldedStates; }
            set { SetProperty(ref _totalModuleShieldedStates, value); }
        }

        // 5. 从站状态,要设置五个从站状态,这样方便使用,不要设置成一个列表,前面数据绑定的时候不是很清晰.

        // 5.1 电阻率国产
        public SlaveState ResistivityPcState { get; set; } = new SlaveState();

        /// <summary>
        /// Napons电阻率状态
        /// </summary>
        public SlaveState NaponsState { get; set; } = new SlaveState();

        // 5.2 尺寸
        public SlaveState SizePcState { get; set; } = new SlaveState();

        // 5.3 崩边隐裂
        public SlaveState EdgeBreakPcState { get; set; } = new SlaveState();

        // 5.4 厚度+线痕+TTV
        public SlaveState ThicknessPcState { get; set; } = new SlaveState();

        // 5.5 上下脏污
        public SlaveState SmudgePcState { get; set; } = new SlaveState();

        // 5.6 隐裂
        public SlaveState SubfissurePcState { get; set; } = new SlaveState();

        /// <summary>
        /// 6. 所有从站的名字还有选中的Index
        /// </summary>
        private ObservableCollection<string> _slaveNames = new ObservableCollection<string>();

        public ObservableCollection<string> SlaveNames
        {
            get { return _slaveNames; }
            set { SetProperty(ref _slaveNames, value); }
        }

        private SlaveState? _currentSalveSelected;

        public SlaveState? CurrentSalveSelected
        {
            get { return _currentSalveSelected; }
            set { SetProperty(ref _currentSalveSelected, value); }
        }


        private int _slaveNameSelectedIndex = 0;

        public int SlaveNameSelectedIndex
        {
            get
            {
                if (_slaveNameSelectedIndex == 0)
                {
                    CurrentSalveSelected = ResistivityPcState;
                }
                return _slaveNameSelectedIndex;
            }
            set
            {
                SetProperty(ref _slaveNameSelectedIndex, value);
                switch (value)
                {
                    case 0:
                        CurrentSalveSelected = ResistivityPcState;
                        break;
                    case 1:
                        CurrentSalveSelected = SizePcState;
                        break;
                    case 2:
                        CurrentSalveSelected = EdgeBreakPcState;
                        break;
                    case 3:
                        CurrentSalveSelected = ThicknessPcState;
                        break;
                    case 4:
                        CurrentSalveSelected = SmudgePcState;
                        break;
                    case 5:
                        CurrentSalveSelected = SubfissurePcState;
                        break;
                }
            }
        }


        // 7. 晶棒统计的类型和对应的计数(首页显示),首先是从数据库获取,如果数据库没有,再以工单为准.
        //  数据库怎么保存呢?  数据库按照晶棒编号去统计,晶棒编号  统计的Level  统计的个数.
        private ObservableCollection<DetectTypeModel> _detectTypeCountList = new ObservableCollection<DetectTypeModel>();

        public ObservableCollection<DetectTypeModel> DetectTypeCountList
        {
            get { return _detectTypeCountList; }
            set { SetProperty(ref _detectTypeCountList, value); }
        }

        // 8. 工单页面第二个表格对应的结果显示
        private ObservableCollection<RecipeConfigModel> _recipeConfigList = new ObservableCollection<RecipeConfigModel>();

        public ObservableCollection<RecipeConfigModel> RecipeConfigList
        {
            get { return _recipeConfigList; }
            set { SetProperty(ref _recipeConfigList, value); }
        }

        // 9. 自动作业停止标志位,自动作业停止的时候这里会设置为True,默认是true
        private bool _workStoppedFlag = true;

        public bool WorkStoppedFlag
        {
            get { return _workStoppedFlag; }
            set { SetProperty(ref _workStoppedFlag, value); }
        }

        // 10. 允许上料和停止上料的标志位,默认是允许上料.
        private bool _allowLoadingFlag = true;

        public bool AllowLoadingFlag
        {
            get { return _allowLoadingFlag; }
            set { SetProperty(ref _allowLoadingFlag, value); }
        }

        // 11.  所有料盒以及料盒的编号,用来显示多选框
        private ObservableCollection<string> _boxNumberList = new ObservableCollection<string>();

        public ObservableCollection<string> BoxNumberList
        {
            get { return _boxNumberList; }
            set { SetProperty(ref _boxNumberList, value); }
        }

        // 料盒选中编号,从0开始
        private int _boxSelectedIndex = 0;

        public int BoxSelectedIndex
        {
            get { return _boxSelectedIndex; }
            set { SetProperty(ref _boxSelectedIndex, value); }
        }



        // 12. 料盒State类型,主要显示料盒信息,注意这里显示的类型,只是最后一个类型.
        private ObservableCollection<BoxesStateModel> _boxesStateList = new ObservableCollection<BoxesStateModel>();

        public ObservableCollection<BoxesStateModel> BoxesStateList
        {
            get { return _boxesStateList; }
            set { SetProperty(ref _boxesStateList, value); }
        }

        // 13. 工单检测规则Rule,主要用来在界面上显示.
        private ObservableCollection<RecipeDectectedRuleModel> _detectedRuleList = new ObservableCollection<RecipeDectectedRuleModel>();

        public ObservableCollection<RecipeDectectedRuleModel> DetectedRuleList
        {
            get { return _detectedRuleList; }
            set { SetProperty(ref _detectedRuleList, value); }
        }


        // 14. 主界面报警信息显示的列表,也是从数据库里面获取,只是获取之后按照插入的Id进行排列,逆序排列.
        private ObservableCollection<AlarmModel> _mainAlarmShowList = new ObservableCollection<AlarmModel>();
        public ObservableCollection<AlarmModel> MainAlarmShowList
        {
            get { return _mainAlarmShowList; }
            set { SetProperty(ref _mainAlarmShowList, value); }
        }



        /// <summary>
        /// 数据初始化
        /// </summary>
        /// <exception cref="NotImplementedException"></exception>
        private void InitializeData()
        {
            #region 1. WorkingInfo 初始化
            var workingInfoResult = LocalDataAccess.GetWorkingInfo();
            if (!workingInfoResult.State)
            {
                LogHelper.WriteLog(LogType.Fatal, workingInfoResult.Message);
                MessageBox.Show(workingInfoResult.Message);
                Application.Current?.Shutdown();
                return;
            }
            var workingInfoData = workingInfoResult.Data!;
            WorkingInfo.FlowerNumber = workingInfoData.FlowerNumber;
            WorkingInfo.ProductionDate = workingInfoData.ProductionDate;
            WorkingInfo.CutNumber = workingInfoData.CutNumber;
            WorkingInfo.CrystalNumber = workingInfoData.CrystalNumber;
            WorkingInfo.TheoryCounts = int.Parse(workingInfoData.TheoryCounts!);
            WorkingInfo.TotalCountFromPc = int.Parse(workingInfoData.TotalCountFromPc!);
            WorkingInfo.PositionNumber = workingInfoData.PositionNumber;
            WorkingInfo.ProductionLot = workingInfoData.ProductionLot;
            WorkingInfo.ProductionRecipe = workingInfoData.ProductionRecipe;
            WorkingInfo.RecipeImportDir = workingInfoData.RecipeImportDir;
            workingInfoData.RecipeImportFile = workingInfoData.RecipeImportFile;
            WorkingInfo.LastCrystalNumber = workingInfoData.LastCrystalNumber;
            GlobalValues.ProductionRecipeName = workingInfoData.ProductionRecipe!;
            GlobalValues.PordutionRecipePath = Path.Combine(GlobalValues.ProductionRecipeDir, GlobalValues.ProductionRecipeName);
            GlobalValues.RecipeImportDir = workingInfoData.RecipeImportDir!;
            GlobalValues.RecipeImportFile = workingInfoData.RecipeImportFile!;
            GlobalValues.CrystalNumberCurrent = workingInfoData.CrystalNumber;
            GlobalValues.LastCrystalNumber = workingInfoData.LastCrystalNumber;
            GlobalValues.PositionNumber = workingInfoData.PositionNumber;
            GlobalValues.ProductionLot = workingInfoData.ProductionLot;
            GlobalValues.CutNumber = workingInfoData.CutNumber;
            GlobalValues.FlowerNumber = workingInfoData.FlowerNumber;

            // 工单下拉菜单加载
            RecipeDirReload();

            #endregion

            #region 2. 屏蔽检测模块初始化
            var moduleShieldStateResult = LocalDataAccess.GetModuleShieldState();
            if (!moduleShieldStateResult.State)
            {
                LogHelper.WriteLog(LogType.Fatal, moduleShieldStateResult.Message);
                MessageBox.Show(moduleShieldStateResult.Message);
                Application.Current?.Shutdown();
                return;
            }

            var moduleShieldStateData = moduleShieldStateResult.Data!;
            foreach (var item in moduleShieldStateData)
            {
                // 将所有的数据都加入到TotalModuleShieldedStates里面去
                // 前面界面可以显示使用.
                ModuleShieldedStateModel model = new ModuleShieldedStateModel();
                model.ModuleName = item.ModuleName;
                model.IsShielded = item.IsShielded;
                TotalModuleShieldedStates.Add(model);
                if (item.IsShielded)
                {
                    ModuleShieldedList.Add(item.ModuleName!);
                }
            }

            // 注意这里有一个坑.就是ObservalCollection<string> 更新的问题.
            // 如果采用下面的这种写法就会有问题.

            //ModuleShieldedList = new ObservableCollection<string>(moduleShieldStateData
            //    .Where(m => m.IsShielded)
            //    .Select(m => m.ModuleName).ToList()!);

            // 采用这种方式就会有问题,具体是什么原因导致的呢? 是因为ObservalCollection<T> 类确实具有自动更新的功能.
            // 但是它是当集合中的元素发生变化的时候(添加,删除,替换等), ObservalCollection<T> 会自动通知相关的绑定进行更新.

            // 如果采用下面这种写法,虽然你使用了ObservalCollection<string> 类型的属性ModuleShieldedList,但是在构造函数中的赋值操作中.
            // 你实际上创建了一个新的ObservalCollection<string> 对象,并将其赋值给ModuleShieldedList属性.这就导致了原始的ObservalCollection<string>
            // 对象与界面上绑定的对象不再是同一个对象,因此界面不会自动更新.

            // 为了确保界面能够正确的更新,你需要对原始的ObservalCollection<string>进行操作,而不是创建一个新的对象并赋值给属性.
            // 当然你可以将ObservalCollection改动如下:
            /*
             * private ObservableCollection<string> _moduleShieldedList;
                public ObservableCollection<string> ModuleShieldedList
                {
                    get { return _moduleShieldedList; }
                    set
                    {
                        _moduleShieldedList = value;
                        OnPropertyChanged(nameof(ModuleShieldedList));
                    }
                }
             */
            #endregion

            #region 3. 初始化从站状态,默认都是断开的

            var slavePcSettingsResult = LocalDataAccess.GetSlaveSettings();
            if (!slavePcSettingsResult.State)
            {
                LogHelper.WriteLog(LogType.Fatal, slavePcSettingsResult.Message);
                MessageBox.Show(slavePcSettingsResult.Message);
                Application.Current?.Shutdown();
                return;
            }

            NaponsState.SlaveName = "1.电阻率";
            NaponsState.IP = "192.168.1.10";
            NaponsState.Port = 10001;
            NaponsState.ConnectedStatus = "已断开";

            var slavePcSettingsDataList = slavePcSettingsResult.Data!;
            var slavePcSetting = slavePcSettingsDataList[0];  // 第一个电阻率
            ResistivityPcState.SlaveName = slavePcSetting.SlaveName;
            ResistivityPcState.IP = slavePcSetting.SlaveIp;
            ResistivityPcState.Port = slavePcSetting.SlavePort;
            ResistivityPcState.ConnectedStatus = "已断开";
            ResistivityPcState.HeartStatus = false;
            ResistivityPcState.ReadyStatus = false;

            slavePcSetting = slavePcSettingsDataList[1];  // 第二个尺寸
            SizePcState.SlaveName = slavePcSetting.SlaveName;
            SizePcState.IP = slavePcSetting.SlaveIp;
            SizePcState.Port = slavePcSetting.SlavePort;
            SizePcState.ConnectedStatus = "已断开";
            SizePcState.HeartStatus = false;
            SizePcState.ReadyStatus = false;


            slavePcSetting = slavePcSettingsDataList[2]; // 第三个崩边
            EdgeBreakPcState.SlaveName = slavePcSetting.SlaveName;
            EdgeBreakPcState.IP = slavePcSetting.SlaveIp;
            EdgeBreakPcState.Port = slavePcSetting.SlavePort;
            EdgeBreakPcState.ConnectedStatus = "已断开";
            EdgeBreakPcState.HeartStatus = false;
            EdgeBreakPcState.ReadyStatus = false;


            slavePcSetting = slavePcSettingsDataList[3]; // 第四个厚度线痕
            ThicknessPcState.SlaveName = slavePcSetting.SlaveName;
            ThicknessPcState.IP = slavePcSetting.SlaveIp;
            ThicknessPcState.Port = slavePcSetting.SlavePort;
            ThicknessPcState.ConnectedStatus = "已断开";
            ThicknessPcState.HeartStatus = false;
            ThicknessPcState.ReadyStatus = false;

            slavePcSetting = slavePcSettingsDataList[4]; // 第五个上下脏污
            SmudgePcState.SlaveName = slavePcSetting.SlaveName;
            SmudgePcState.IP = slavePcSetting.SlaveIp;
            SmudgePcState.Port = slavePcSetting.SlavePort;
            SmudgePcState.ConnectedStatus = "已断开";
            SmudgePcState.HeartStatus = false;
            SmudgePcState.ReadyStatus = false;


            #endregion

            #region 4. 初始化所有的从站名称,主要用来选择控制具体的从站的
            SlaveNames = new ObservableCollection<string>();
            for (int i = 0; i < slavePcSettingsDataList.Count; i++)
            {
                //注意种类使用Append的时候有坑
                // List和ObservalCollection中的Add和Append的区别?
                // Append是追加到末尾,返回一个新的变量,不修改原来的集合.
                // Add是直接就地修改
                //SlaveNames.Append(slavePcSettingsDataList[i].SlaveName!);
                SlaveNames.Add(slavePcSettingsDataList[i].SlaveName!);
            }

            #endregion

            #region 5. 初始化工单信息
            /*
             * 首先是根据检测配方.去获取要检测的类型.首先是去找对应晶棒是否有统计计数.
             * 如果有,就直接显示,如果没有.就创建一个默认的去显示.所以这里还是要去搞一个默认的显示.
             */
            // Setp1. 先根据工单文件路径去获取工单信息,如果工单的路径不存在,工单信息获取失败,就按照默认的工单.
            // 默认的工单在工程的项目中.
            InitRecipe();

            #endregion

            #region 6. 初始化料盒信息

            // 6.1 下拉菜单料盒名称初始化,具体的名字在GlobalValues中
            BoxNumberList.Clear();
            foreach (string s in GlobalValues.BoxNames)
            {
                BoxNumberList.Add(s);
            }

            #endregion

            #region 7. 料盒里面的数据初始化
            var boxStateResult = LocalDataAccess.GetBoxesStates();


            if (!boxStateResult.State)
            {

                LogHelper.WriteLog(LogType.Fatal, boxStateResult.Message);
                MessageBox.Show(boxStateResult.Message);
                Application.Current?.Shutdown();
                return;
            }
            var boxStateData = boxStateResult.Data!;

            for (int i = 0; i < boxStateData.Count; i++)
            {
                var item = boxStateData[i];
                BoxesStateModel model = new BoxesStateModel();
                model.BoxNumber = int.Parse(item.BoxNumber!);
                model.CountMax = int.Parse(item.CountMax!);
                model.LevelType = item.LevelType;
                model.UpCount = int.Parse(item.Count!);
                model.DownCount = int.Parse(item.Count!);
                BoxesStateList.Add(model);
            }

            // 如果发现PLC在线,就将料盒最大值设置进去.
            #endregion

            #region 8. 汇总目录初始化
            FileOperateHelper.CreateSummaryDir();
            #endregion

        }
        /// <summary>
        /// 初始化工单信息
        /// </summary>
        /// <exception cref="NotImplementedException"></exception>
        public void InitRecipe()
        {
            #region 1. 首页上的类型检测加载
            // 先声明出来,确保加载的时候不会重复加载,如果这个数据前面已经加载了,就不用重复加载
            DataResult<List<DetectedLevelEntity>>? recipeDetectedLevelResult = null;
            // 1. 先通过数据库去获取晶棒的统计信息,如果这里获取失败,再从工单上获取晶棒信息,然后按照全部是0的方式保存一份新数据.
            DataResult<List<CrystalLevelCountEntity>> dataResult = LocalDataAccess.GetCrystalLevelCount(GlobalValues.CrystalNumberCurrent);
            // 工单初始化的时候还要注意等级Level对应的等级描述,包括工单的还有自定义的.
            GlobalValues.DetectedTypeNamesTotal.Clear(); // 清空
            DetectTypeCountList.Clear();
            string PordutionRecipePathUsed;
            if (File.Exists(GlobalValues.PordutionRecipePath))
            {
                PordutionRecipePathUsed = GlobalValues.PordutionRecipePath;
            }
            else
            {
                PordutionRecipePathUsed = GlobalValues.PordutionRecipePathDefault;
            }

            if (!dataResult.State)
            {
                // 如果从数据库获取失败,就从工单那里去加载,工单加载的时候要注意一下几点
                recipeDetectedLevelResult = LocalDataAccess.GetRecipeDetectedLevel(PordutionRecipePathUsed);
                if (!recipeDetectedLevelResult.State)
                {
                    LogHelper.WriteLog(LogType.Fatal, recipeDetectedLevelResult.Message);
                    MessageBox.Show(recipeDetectedLevelResult.Message);
                }
                // 这里将数据显示到对应的ViewModel上去.
                // 首先是筛选首页上的. 遍历这个列表,然后组装首页要显示的列表.
                List<DetectedLevelEntity> resList = recipeDetectedLevelResult.Data!;
                foreach (var levelRes in resList)
                {
                    if (levelRes.IsUsed)
                    {
                        var temp = new DetectTypeModel();
                        temp.LevelIndex = levelRes.Level;
                        temp.DetectedType = $"{levelRes.Level}.{levelRes.TypeDescription}";
                        temp.DetectedCount = "0";
                        DetectTypeCountList.Add(temp);
                        // 1. 增加到全局变量中去.
                        GlobalValues.DetectedTypeNamesTotal[levelRes.Level] = levelRes.TypeDescription!;
                    }
                }
                // 将后面固定要检测的类型也加上
                foreach (KeyValuePair<int, string> item in GlobalValues.DetectedTypeNamesDefineSelf)
                {
                    var temp = new DetectTypeModel();
                    temp.LevelIndex = item.Key;
                    temp.DetectedType = item.Value;
                    temp.DetectedCount = "0";
                    DetectTypeCountList.Add(temp);
                    // 增加到全局变量中去
                    GlobalValues.DetectedTypeNamesTotal[item.Key] = item.Value;
                }

                // 将DetectTypeCountList转换为CrystalLevelCountEntity的实体类,好去做插入数据的操作
                List<CrystalLevelCountEntity> detectedLevelEntities = new List<CrystalLevelCountEntity>();
                foreach (var temp in DetectTypeCountList)
                {
                    var levelCountObj = new CrystalLevelCountEntity();
                    levelCountObj.CrystalNumber = GlobalValues.CrystalNumberCurrent;
                    levelCountObj.LevelIndex = temp.LevelIndex;
                    levelCountObj.LevelType = temp.DetectedType;
                    levelCountObj.Count = temp.DetectedCount;
                    detectedLevelEntities.Add(levelCountObj);
                }

                DataResult writeDataToSqlResult = LocalDataAccess.InsertDataToCrystalLevelCountTb(detectedLevelEntities);
                if (!writeDataToSqlResult.State)
                {
                    LogHelper.WriteLog(LogType.Fatal, writeDataToSqlResult.Message);
                    MessageBox.Show(writeDataToSqlResult.Message);
                    Application.Current?.Shutdown();
                    return;
                }
            }
            else
            {
                foreach (var item in dataResult.Data!)
                {
                    var temp = new DetectTypeModel();
                    temp.LevelIndex = item.LevelIndex;
                    temp.DetectedType = item.LevelType;
                    GlobalValues.DetectedTypeNamesTotal[item.LevelIndex] = item.LevelType!;
                    temp.DetectedCount = item.Count;
                    DetectTypeCountList.Add(temp);
                }
            }

            // 他们开启的检测等级的个数,一般来说是15个等级. 如果检测等级小于这个数据,要补全后面的,没有数据的选项.
            #endregion

            // 2 先获取的是类型和统计的个数,注意这里分为主页上显示的类型还有就是分页上显示的类型.
            // 2. 主页上类型对应的个数,就根据名称去定位就行了.
            if (recipeDetectedLevelResult == null)
            {
                recipeDetectedLevelResult = LocalDataAccess.GetRecipeDetectedLevel(PordutionRecipePathUsed);
            }
            if (!recipeDetectedLevelResult.State)
            {
                LogHelper.WriteLog(LogType.Fatal, recipeDetectedLevelResult.Message);
                MessageBox.Show(recipeDetectedLevelResult.Message);
                Application.Current?.Shutdown();
                return;
            }

            // 将工单上的数据转换到Model上去以及更新料盒对应类型描述(这个如何确定. 根据料盒去找,然后去更新数据库.
            // 只更新类型,不更新数量.
            string recipeName = "";
            if (File.Exists(GlobalValues.PordutionRecipePath))
            {
                recipeName = Path.GetFileName(GlobalValues.PordutionRecipePath);
            }
            else
            {
                recipeName = Path.GetFileName(GlobalValues.PordutionRecipePathDefault);
            }
            // 全局的等级和料盒映射关系,如果等级大于30就认为是异常的.
            GlobalValues.LevelBoxMap.Clear();
            // 直流的全部按照19这个数值去搞.
            GlobalValues.LevelBoxMap[30] = new List<int>() { 19 };
            // 清空数据,防止程序一直重复加载工单.
            RecipeConfigList.Clear();
            foreach (var item in recipeDetectedLevelResult.Data!)
            {
                var temp = new RecipeConfigModel();
                temp.Level = item.Level;
                temp.IsUsed = item.IsUsed ? "TRUE" : "FALSE";
                temp.LevelType = item.LevelType;
                temp.TypeDescription = item.TypeDescription;
                temp.BoxesInto = item.BoxesInto;
                RecipeConfigList.Add(temp);
                // 更新料盒对应关系到数据库,如果现场的等级和对应的料盒关系一直维持不变,就是不经常改动,这里可以不变.
                UpdateBoxesStateNumberToSql(item.TypeDescription!, item.BoxesInto);
                // 将映射关系保存到全局字典当中
                if (!string.IsNullOrEmpty(temp.BoxesInto))
                {
                    GlobalValues.LevelBoxMap[temp.Level] = item.BoxesInto!.Split(',').Select(int.Parse).ToList();
                }
            }
            GlobalValues.RecipeConfig = RecipeConfigList.ToList();

            GlobalValues.NormalDetectedTotalCounts = DetectTypeCountList.Count - GlobalValues.DetectedTypeNamesDefineSelf.Count;
            // 3. 检测规则加载,目前的设计思路是每项都创建一个字典.对应检测等级和数值.
            DetectedRuleInit();

            // 4. 测试规则是否正确,用一个假数据去测试
            //TestDetectedLevelRuleIsRight();
        }


        /// <summary>
        /// 测试分选规则是否正确,用假的数据去测试
        /// </summary>
        private void TestDetectedLevelRuleIsRight()
        {
            string TestData = "0";
            string TestName = "上脏污";
            List<int> lengthMaxLevel = CalcDetectedLevel.CalcDetectedLevelByRule(DetectedRuleHelper.SumdgeUpExistRule, TestData);
            LogHelper.WriteLog(LogType.Info, $"{TestName}对应的数据 {TestData} 的测试结果: {string.Join(",", lengthMaxLevel)}");
        }

        private DataResult DetectedRuleInit()
        {

            DataResult result = new DataResult();
            try
            {
                string PordutionRecipePathUsed;
                if (File.Exists(GlobalValues.PordutionRecipePath))
                {
                    PordutionRecipePathUsed = GlobalValues.PordutionRecipePath;
                }
                else
                {
                    PordutionRecipePathUsed = GlobalValues.PordutionRecipePathDefault;
                }
                DataResult<List<List<string>>> ruleContentResult = LocalDataAccess.GetRecipeDectectedRuleContent(PordutionRecipePathUsed);
                if (!ruleContentResult.State)
                {
                    result.Message = ruleContentResult.Message;
                    return result;
                }
                // 1. 先去清空检测规则文件里面的内容.
                FileOperateHelper.CreateAnEmptyFile(GlobalValues.DetectedRulePath);

                var ruleContentData = ruleContentResult.Data;
                DetectedRuleList.Clear(); // 先清空,防止重新加载的时候数据出现
                foreach (var ruleContent in ruleContentData!)
                {
                    int dataLen = ruleContent.Count;
                    var modelObj = new RecipeDectectedRuleModel()
                    {
                        DetectedName = 0 < dataLen ? ruleContent[0].Trim('\"') : "",
                        Description = 1 < dataLen ? ruleContent[1].Trim('\"') : "",
                        Unit = 2 < dataLen ? ruleContent[2].Trim('\"') : "",
                        Precis = 3 < dataLen ? ruleContent[3].Trim('\"') : "",
                        Level01Val = 4 < dataLen ? ruleContent[4].Trim('\"') : "",
                        Level02Val = 5 < dataLen ? ruleContent[5].Trim('\"') : "",
                        Level03Val = 6 < dataLen ? ruleContent[6].Trim('\"') : "",
                        Level04Val = 7 < dataLen ? ruleContent[7].Trim('\"') : "",
                        Level05Val = 8 < dataLen ? ruleContent[8].Trim('\"') : "",
                        Level06Val = 9 < dataLen ? ruleContent[9].Trim('\"') : "",
                        Level07Val = 10 < dataLen ? ruleContent[10].Trim('\"') : "",
                        Level08Val = 11 < dataLen ? ruleContent[11].Trim('\"') : "",
                        Level09Val = 12 < dataLen ? ruleContent[12].Trim('\"') : "",
                        Level10Val = 13 < dataLen ? ruleContent[13].Trim('\"') : "",
                        Level11Val = 14 < dataLen ? ruleContent[14].Trim('\"') : "",
                        Level12Val = 15 < dataLen ? ruleContent[15].Trim('\"') : "",
                        Level13Val = 16 < dataLen ? ruleContent[16].Trim('\"') : "",
                        Level14Val = 17 < dataLen ? ruleContent[17].Trim('\"') : "",
                        Level15Val = 18 < dataLen ? ruleContent[18].Trim('\"') : "",
                        Level16Val = 19 < dataLen ? ruleContent[19].Trim('\"') : "",
                        Level17Val = 20 < dataLen ? ruleContent[20].Trim('\"') : "",
                        Level18Val = 21 < dataLen ? ruleContent[21].Trim('\"') : "",
                        Level19Val = 22 < dataLen ? ruleContent[22].Trim('\"') : "",
                        Level20Val = 23 < dataLen ? ruleContent[23].Trim('\"') : "",
                        Level21Val = 24 < dataLen ? ruleContent[24].Trim('\"') : "",
                    };
                    DetectedRuleList.Add(modelObj);
                    DataResult res = DetectedRuleHelper.LoadRuleToDictionary(modelObj);
                    if (!res.State)
                    {
                        LogHelper.WriteLog(LogType.Info, $"规则加载到字典里面出错: {res.Message}");
                    }
                }
                result.State = true;
            }
            catch (Exception ex)
            {
                result.Message = ExceptionHelper.GetFullException(ex, MethodBase.GetCurrentMethod()!);
            }
            return result;
        }

        /// <summary>
        /// 更新数据到BoxesStateNumber表中.
        /// </summary>
        /// <param name="levelType"></param>
        /// <param name="boxesInto"></param>
        /// <exception cref="NotImplementedException"></exception>
        private bool UpdateBoxesStateNumberToSql(string levelType, string? boxesInto)
        {
            if (!string.IsNullOrEmpty(boxesInto))
            {
                string[] boxesArray = boxesInto.Split(",");
                foreach (var box in boxesArray)
                {
                    LocalDataAccess.UpdateBoxesStateTb("BoxNumber", box, "LevelType", levelType);
                }
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 切换用户的命令
        /// </summary>
        public Command ChangeUserCommand { get; set; }

        private void DoChangeUserCommand(object obj)
        {
            // 切换用户的时候,弹窗显示切换窗口.然后显示这个窗口.
            ChangeUserWindow window = new ChangeUserWindow();
            UserModel user = new UserModel()
            {
                Username = "",
                Password = ""
            };
            window.DataContext = user;
            window.ShowDialog();
        }

        /// <summary>
        /// 清空指定料盒
        /// </summary>
        public Command ClearOneBoxCommand { get; private set; }
        private void DoClearOneBoxTask(object obj)
        {
            DataResult result;
            if (obj != null)
            {
                if (obj.ToString() == "All")
                {
                    for (int i = 0; i < BoxesStateList.Count; i++)
                    {
                        result = PlcCommunication.Instance.ClearOneBox(i + 1);
                        if (!result.State)
                        {
                            LogHelper.WriteLog(LogType.Fatal, result.Message);
                        }
                    }
                }
                else if (obj.ToString() == "Direction")
                {
                    result = PlcCommunication.Instance.ClearOneBox(BoxesStateList.Count);
                    if (!result.State)
                    {
                        LogHelper.WriteLog(LogType.Fatal, result.Message);
                    }
                }
                else
                {
                    result = PlcCommunication.Instance.ClearOneBox(BoxSelectedIndex + 1);
                    if (!result.State)
                    {
                        LogHelper.WriteLog(LogType.Fatal, result.Message);
                    }
                }
            }
        }


        private bool _pLCRunningState;

        public bool PLCRunningState
        {
            get { return _pLCRunningState; }
            set
            {
                // 如果设备的运行状态发生了改变,就通知PLC,停止或者是运行.
                if (value != PLCRunningState)
                {
                    if (value)
                    {
                        // 运行PLC
                        IsCheckedColor = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#00AC6A"));
                    }
                    else
                    {
                        // 停止PLC
                    }
                }
                SetProperty(ref _pLCRunningState, value);
            }
        }


        private bool _pLcStopState;

        public bool PLCStopState
        {
            get { return _pLcStopState; }
            set
            {
                if (value != PLCStopState)
                {
                    if (value)
                    {
                        // 停止PLC
                        IsCheckedColor = new SolidColorBrush(Colors.Red);
                    }
                    else
                    {
                        // 
                    }
                }
                SetProperty(ref _pLcStopState, value);
            }
        }



        private SolidColorBrush? _runningRadioColor;

        public SolidColorBrush? RunningRadioColor
        {
            get { return _runningRadioColor; }
            set { SetProperty(ref _runningRadioColor, value); }
        }

        private SolidColorBrush? _isCheckedColor;

        public SolidColorBrush? IsCheckedColor
        {
            get { return _isCheckedColor; }
            set { SetProperty(ref _isCheckedColor, value); }
        }

        /// <summary>
        /// 下一个晶棒编号是否已经录入.如果录入了,就将切换下一单按变成可以使用
        /// </summary>
        private bool _nextCrystalIsOK;

        public bool NextCrystalIsOK
        {
            get { return _nextCrystalIsOK; }
            set { SetProperty(ref _nextCrystalIsOK, value); }
        }


        /// <summary>
        /// 3D工位标定状态 OK 和 NG两种状态,默认是NG状态.就是软件每次打开的时候都是NG状态,然后要3D标定之后才能是OK状态.
        /// </summary>
        private string? _calibration3DStatus;

        public string? Calibration3DStatus
        {
            get { return _calibration3DStatus; }
            set
            {
                if (value != "OK")
                {
                    Calibration3DStatusColor = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF0000"));
                }
                else
                {
                    Calibration3DStatusColor = new SolidColorBrush(Colors.ForestGreen);
                }
                SetProperty(ref _calibration3DStatus, value);
            }
        }

        private SolidColorBrush? _calibrationStatusColor;

        public SolidColorBrush? Calibration3DStatusColor
        {
            get { return _calibrationStatusColor; }
            set { SetProperty(ref _calibrationStatusColor, value); }
        }

        /// <summary>
        /// 当前晶棒是否选中
        /// </summary>
        private bool _crystalCurrentIsChecked;

        public bool CrystalCurrentIsChecked
        {
            get { return _crystalCurrentIsChecked; }
            set
            {
                SetProperty(ref _crystalCurrentIsChecked, value);
            }
        }

        private bool _crystalLastIsChecked;

        public bool CrystalLastIsChecked
        {
            get { return _crystalLastIsChecked; }
            set
            {
                SetProperty(ref _crystalLastIsChecked, value);
            }
        }

        // 下面主要是关于那个切换配方逻辑相关的变量.

        /// <summary>
        /// 工单列表,目录中所有的工单选项.
        /// </summary>
        private ObservableCollection<string> _recipeList = new ObservableCollection<string>();

        public ObservableCollection<string> RecipeList
        {
            get { return _recipeList; }
            set { SetProperty(ref _recipeList, value); }
        }

        /// <summary>
        /// 当前选中工单
        /// </summary>
        private string? _selectedRecipe;

        public string? SelectedRecipe
        {
            get { return _selectedRecipe; }
            set { SetProperty(ref _selectedRecipe, value); }
        }

        /// <summary>
        /// 当前选中工单的Index
        /// </summary>
        private int _selectedIndex;

        public int SelectedIndex
        {
            get { return _selectedIndex; }
            set { SetProperty(ref _selectedIndex, value); }
        }

        /// <summary>
        /// 配方目录加载
        /// </summary>
        public void RecipeDirReload()
        {
            try
            {
                DirectoryInfo root = new DirectoryInfo(GlobalValues.ProductionRecipeDir);
                RecipeList.Clear();
                FileInfo[] fileArray = root.GetFiles();
                Array.Sort(fileArray, delegate (FileInfo x, FileInfo y) { return y.CreationTime.CompareTo(x.CreationTime); });
                foreach (FileInfo item in fileArray)
                {
                    string name = item.Name;
                    if (!string.IsNullOrEmpty(name) && !name.EndsWith(".txt"))
                    {
                        RecipeList.Add(name);
                    }
                }
                if (RecipeList.Count < 0)
                {
                    MessageBox.Show("作业工单目录为空,请先导入一个工单到生产作业目录", "工单目录出错提示框", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }

                // 看看当前选择的工单是否在这个目录中,如果不在的话,要默认选中一个工单. 这个时候就要选择一个默认工单.
                if (!RecipeList.Contains(WorkingInfo.ProductionRecipe!))
                {
                    List<string> updateFields = new List<string>()
                    {
                        "ProductionRecipe"
                    };
                    List<string> updateVals = new List<string>()
                    {
                        RecipeList[0]
                    };
                    DataResult result = LocalDataAccess.UpdateWorkingInfo(updateFields, updateVals);
                    if (!result.State)
                    {
                        MessageBox.Show($"数据库操作失败: {result.Message}", "数据库操作失败提示框", MessageBoxButton.OK, MessageBoxImage.Error);
                        return;
                    }

                    // 将当前使用配方全局变量更新
                    GlobalValues.ProductionRecipeName = RecipeList[0];
                    GlobalValues.PordutionRecipePath = Path.Combine(GlobalValues.ProductionRecipeDir, GlobalValues.ProductionRecipeName);

                    SelectedIndex = 0;
                    SelectedRecipe = RecipeList[0];
                }
                else
                {
                    SelectedIndex = RecipeList.IndexOf(WorkingInfo.ProductionRecipe!);
                    SelectedRecipe = WorkingInfo.ProductionRecipe!;
                }
            }
            catch (Exception ex)
            {
                LogHelper.WriteLog(LogType.Exception, ExceptionHelper.GetFullException(ex, MethodBase.GetCurrentMethod()!));
                MessageBox.Show($"{ExceptionHelper.GetFullException(ex, MethodBase.GetCurrentMethod()!)}", "配方目录加载错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        /// <summary>
        /// 配方切换下拉菜单是否可用
        /// </summary>
        private bool _recipeComboxCanUse;

        public bool RecipeComboxCanUse
        {
            get { return _recipeComboxCanUse; }
            set { SetProperty(ref _recipeComboxCanUse, value); }
        }

        /// <summary>
        /// Cv按钮的名称
        /// </summary>
        private string? _cvButtonName = "CV清洗";

        public string? CvButtonName
        {
            get { return _cvButtonName; }
            set { SetProperty(ref _cvButtonName, value); }
        }

        /// <summary>
        /// 是否允许上料
        /// </summary>
        private bool _loadIsOpen = false;

        public bool LoadIsOpen
        {
            get { return _loadIsOpen; }
            set { SetProperty(ref _loadIsOpen, value); }
        }


        /// <summary>
        /// 节拍
        /// </summary>
        private string? _beat;

        public string? Beat
        {
            get { return _beat; }
            set
            {
                value = $"{value} ms";
                SetProperty(ref _beat, value);
            }
        }


        /// <summary>
        /// 产能
        /// </summary>
        private string? _capacity;

        public string? Capacity
        {
            get { return _capacity; }
            set
            {
                value = $"{value} pcs";
                SetProperty(ref _capacity, value);
            }
        }


        /// <summary>
        /// 是否固定料盒
        /// </summary>
        private bool _isFixedBox;

        public bool IsFixedBox
        {
            get { return _isFixedBox; }
            set { SetProperty(ref _isFixedBox, value); }
        }

        // 指定料盒
        private int boxInto;

        public int BoxInto
        {
            get { return boxInto; }
            set { SetProperty(ref boxInto, value); }
        }


        /// <summary>
        /// 固定料盒的值
        /// </summary>
        private string? _boxNumberFixed;

        public string? BoxNumberFixed
        {
            get { return _boxNumberFixed; }
            set { SetProperty(ref _boxNumberFixed, value); }
        }


        public Command ClearAlarmCommand { get; set; }
        private void DoClearAlarmTask(object obj)
        {
            // 清空报警
            LocalDataAccess.DeleteTableByName(TableNames.AlarmMainShowTb);
        }
    }
}
